package com.engine.salary.service.impl;

import com.engine.common.util.ServiceUtil;
import com.engine.core.impl.Service;
import com.engine.salary.constant.SalaryDefaultTenantConstant;
import com.engine.salary.entity.datacollection.dto.VariableItemListDTO;
import com.engine.salary.entity.datacollection.param.VariableItemQueryParam;
import com.engine.salary.entity.datacollection.param.VariableItemSaveParam;
import com.engine.salary.entity.datacollection.po.VariableItemPO;
import com.engine.salary.entity.salaryformula.po.FormulaPO;
import com.engine.salary.entity.salaryformula.po.FormulaVar;
import com.engine.salary.enums.salaryitem.SalaryDataTypeEnum;
import com.engine.salary.exception.SalaryRunTimeException;
import com.engine.salary.mapper.datacollection.VariableItemMapper;
import com.engine.salary.service.*;
import com.engine.salary.util.SalaryEntityUtil;
import com.engine.salary.util.SalaryI18nUtil;
import com.engine.salary.util.db.MapperProxyFactory;
import com.engine.salary.util.page.PageInfo;
import com.engine.salary.util.page.SalaryPageUtil;
import com.engine.salary.util.valid.RuntimeTypeEnum;
import com.engine.salary.util.valid.ValidUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeanUtils;
import weaver.conn.util.IdGenerator;
import weaver.hrm.User;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Harryxzy
 * @ClassName VariableItemServiceImpl
 * @date 2024/08/07 9:31
 * @description 浮动薪酬项目
 */
public class VariableItemServiceImpl extends Service implements VariableItemService {

    private VariableItemMapper getVariableItemMapper(){
        return MapperProxyFactory.getProxy(VariableItemMapper.class);
    }

    private SalaryFormulaService getSalaryFormulaService(User user) {
        return ServiceUtil.getService(SalaryFormulaServiceImpl.class, user);
    }

    private SalaryItemService getSalaryItemService(User user) {
        return ServiceUtil.getService(SalaryItemServiceImpl.class, user);
    }

    private VariableArchiveItemService getVariableArchiveItemService(User user) {
        return ServiceUtil.getService(VariableArchiveItemServiceImpl.class, user);
    }

    private TaxAgentService getTaxAgentService(User user) {
        return ServiceUtil.getService(TaxAgentServiceImpl.class, user);
    }


    @Override
    public VariableItemPO getById(Long id) {
        if (id == null) {
            return null;
        }
        return getVariableItemMapper().getById(id);
    }

    @Override
    public List<VariableItemPO> listByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return getVariableItemMapper().listSome(VariableItemPO.builder().ids(ids).build());
    }

    /**
     * 获取浮动薪酬项目
     * @return
     */
    @Override
    public List<VariableItemPO> listAll() {
        return getVariableItemMapper().listAll();
    }

    /**
     * 获取浮动薪资项目列表（分页）
     *
     * @param queryParam
     * @return
     */
    @Override
    public PageInfo<VariableItemListDTO> listPage(VariableItemQueryParam queryParam) {
        List<VariableItemPO> variableItemPOS = listAll();
        if (StringUtils.isNotBlank(queryParam.getItemName())) {
            variableItemPOS = variableItemPOS.stream().filter(po -> po.getName().contains(queryParam.getItemName())).collect(Collectors.toList());
        }
        // 查询不可删除的code、id
        SalaryItemServiceImpl.UsingItem usingItem = getSalaryItemService(user).getUsingItem();
        List<String> usingCodes = usingItem.getUsingCodes();
        List<Long> usingVariableItemIds = getVariableArchiveItemService(user).listUsingItems();

        List<VariableItemListDTO> variableItemDTOList = variableItemPOS.stream()
                .map(po -> VariableItemListDTO.builder()
                        .id(po.getId())
                        .name(po.getName())
                        .dataType(SalaryDataTypeEnum.parseByValue(po.getDataType()).getDefaultLabel())
                        .canDelete( !(usingCodes.contains(po.getCode()) || usingVariableItemIds.contains(po.getId())) )
                        .build())
                .collect(Collectors.toList());
        return SalaryPageUtil.buildPage(queryParam.getCurrent(), queryParam.getPageSize(), variableItemDTOList, VariableItemListDTO.class);
    }

    private List<VariableItemPO> listByName(String name) {
        return getVariableItemMapper().listSome(VariableItemPO.builder().name(name).build());
    }

    @Override
    public Integer save(VariableItemSaveParam saveParam) {
        // 名称不能和已有的自定义薪资项目重名
        List<VariableItemPO> variableItemPOS = listByName(saveParam.getName());
        if (CollectionUtils.isNotEmpty(variableItemPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "项目名称已存在，请重新命名"));
        }
        Date now = new Date();
        long id = com.engine.salary.util.db.IdGenerator.generate();
        VariableItemPO variableItemPO = VariableItemPO.builder()
                .id(id)
                .name(saveParam.getName())
                .code(IdGenerator.getUUID())
                .dataType(saveParam.getDataType())
                .creator((long) user.getUID())
                .deleteType(NumberUtils.INTEGER_ZERO)
                .createTime(now)
                .updateTime(now)
                .tenantKey(SalaryDefaultTenantConstant.DEFAULT_TENANT_KEY)
                .build();
        return getVariableItemMapper().insertIgnoreNull(variableItemPO);

    }

    @Override
    public VariableItemPO update(VariableItemSaveParam saveParam) {
        ValidUtil.doValidator(saveParam, RuntimeTypeEnum.UPDATE);
        // 查询薪资项目，判断薪资项目是否存在
        VariableItemPO variableItemPO = getById(saveParam.getId());
        if (Objects.isNull(variableItemPO)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "参数错误，项目不存在或已被删除"));
        }
        // 名称不能和已有的自定义薪资项目重名
        List<VariableItemPO> variableItemPOS = listByName(saveParam.getName());
        boolean nameExist = variableItemPOS.stream().anyMatch(e -> !Objects.equals(saveParam.getId(), e.getId()));
        if (nameExist) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(98326, "项目名称已存在，请重新命名"));
        }

        // 更新薪资项目
        VariableItemPO newVariableItemPO = new VariableItemPO();
        BeanUtils.copyProperties(variableItemPO, newVariableItemPO);
        newVariableItemPO.setName(saveParam.getName());
        newVariableItemPO.setDataType(saveParam.getDataType());
        newVariableItemPO.setUpdateTime(new Date());
        getVariableItemMapper().update(newVariableItemPO);

        //改名后更新公式
        String oldName = variableItemPO.getName();
        String newName = saveParam.getName();
        if (!StringUtils.equals(oldName, newName)) {
            String itemPrefix = "variableItem_";
            String fieldNamePrefix = "{浮动薪资项目.%s}";
            changeName(variableItemPO, oldName, newName, itemPrefix, fieldNamePrefix);
        }
        return variableItemPO;
    }

    private void changeName(VariableItemPO variableItemPO, String oldName, String newName, String itemPrefix, String fieldNamePrefix) {
        String code = itemPrefix + variableItemPO.getCode();
        Date now = new Date();
        List<FormulaVar> formulaVars = getSalaryFormulaService(user).listByCode(code);
        formulaVars.forEach(v -> {
            FormulaVar formulaVar = FormulaVar.builder()
                    .id(v.getId())
                    .name(newName)
                    .fieldName(String.format(fieldNamePrefix, newName))
                    .updateTime(now)
                    .build();
            getSalaryFormulaService(user).updateVar(formulaVar);
        });

        List<Long> formulaIds = SalaryEntityUtil.properties(formulaVars, FormulaVar::getFormulaId, Collectors.toList());
        List<FormulaPO> formulaPOS = getSalaryFormulaService(user).listByIds(formulaIds);
        formulaPOS.forEach(f -> {
            String formula = f.getFormula();
            formula = formula.replace(String.format(fieldNamePrefix, oldName), String.format(fieldNamePrefix, newName));
            FormulaPO formulaPO = FormulaPO.builder()
                    .id(f.getId())
                    .formula(formula)
                    .updateTime(now)
                    .build();
            getSalaryFormulaService(user).update(formulaPO);
        });
    }

    @Override
    public void deleteItems(List<Long> itemIds) {
        if (CollectionUtils.isEmpty(itemIds)) {
            return;
        }

        SalaryItemServiceImpl.UsingItem usingItem = getSalaryItemService(user).getUsingItem();
        List<String> usingCodes = usingItem.getUsingCodes();

        // 查询薪资项目
        List<VariableItemPO> variableItemPOS = listByIds(itemIds);
        if (CollectionUtils.isEmpty(variableItemPOS)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "参数错误，浮动薪资项目不存在或已被删除"));
        }
        List<String> codes = SalaryEntityUtil.properties(variableItemPOS, VariableItemPO::getCode, Collectors.toList());
        if (CollectionUtils.containsAny(usingCodes, codes)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "公式正在使用该浮动薪资项目，不允许删除"));
        }
        // 查询浮动薪酬档案中已使用的浮动薪资项目
        List<Long> usingVariableItemIds = getVariableArchiveItemService(user).listUsingItems();
        if (CollectionUtils.containsAny(usingVariableItemIds, itemIds)) {
            throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(0, "浮动薪资档案中正在使用该薪资项目，不允许删除"));
        }
        itemIds.forEach(id -> getVariableItemMapper().delete(VariableItemPO.builder().id(id).build()));
    }

    @Override
    public VariableItemListDTO getDetail(Long id) {
        if (id == null) {
            return null;
        }
        VariableItemPO variableItemPO = getVariableItemMapper().getById(id);
        return VariableItemListDTO.builder()
                .id(variableItemPO.getId())
                .name(variableItemPO.getName())
                .dataType(variableItemPO.getDataType())
                .build();
    }
}
